package com.hzw.saas.common.util;

import cn.hutool.core.codec.Base64;
import cn.hutool.core.lang.Assert;
import cn.hutool.core.net.URLEncoder;
import cn.hutool.core.util.StrUtil;
import cn.hutool.crypto.digest.DigestUtil;
import cn.hutool.crypto.digest.HmacAlgorithm;
import com.hzw.saas.common.util.constants.PathConsts;
import lombok.experimental.UtilityClass;
import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpMethod;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.List;
import java.util.Map;

/**
 * @author zzl
 * @since 08/20/2021
 */
@UtilityClass
@Slf4j
public class SignatureUtil {

    public static final URLEncoder URL_ENCODER = URLEncoder.ALL;
    public static final Charset CHARSET = StandardCharsets.UTF_8;

    public static final String TIME_PATTERN = "yyyy-MM-dd'T'HH:mm:ss'Z'";
    public static final String EMPTY_REQUEST_BODY_MD5 = "d41d8cd98f00b204e9800998ecf8427e";

    /**
     * 构造路径请求参数
     * 按参数名升序排序
     *
     * @param urlParams 请求参数
     * @return 路径参数字符串
     */
    public static StringBuilder generateUrlParamsStr(Map<String, String> urlParams) {
        List<String> keys = new ArrayList<>(urlParams.keySet());
        keys.sort(String::compareTo);
        StringBuilder urlParamsBuilder = new StringBuilder();
        for (String key : keys) {
            String value = urlParams.get(key);
            if (StrUtil.isNotBlank(urlParamsBuilder))
                urlParamsBuilder.append("&");
            key = URL_ENCODER.encode(key, CHARSET);
            value = URL_ENCODER.encode(value, CHARSET);
            urlParamsBuilder.append(key).append("=").append(value);
        }
        log.debug("url params string: {}", urlParamsBuilder);
        return urlParamsBuilder;
    }

    /**
     * 构造被签名串
     * 被签名串的构造规则为: 被签名串 = HTTP请求方式 + “\n” + uri + “\n” + url请求参数 + “\n” + md5(requests_body)
     * <p>
     * md5(requests_body)：请求体的md5值
     * get请求，requests_body为空字符串，计算出的md5值固定为：d41d8cd98f00b204e9800998ecf8427e
     * post请求，requests_body是请求body的json字符串的MD5值
     *
     * @param httpMethod 请求方式
     * @param uri        uri
     * @param paramsStr  url请求参数
     * @param reqBody    请求体(Json字符串)
     * @return 被签名字符串
     */
    public static String generateString2Signature(HttpMethod httpMethod, String uri, String paramsStr, String reqBody) {
        String factor = "\n";
        uri = StrUtil.addPrefixIfNot(uri, PathConsts.SLASH);
        uri = StrUtil.addSuffixIfNot(uri, PathConsts.SLASH);
        String reqBodyMd5 = StrUtil.isBlank(reqBody) ? EMPTY_REQUEST_BODY_MD5 : DigestUtil.md5Hex(reqBody);
        String string2Signature = httpMethod.name()
            .concat(factor).concat(uri)
            .concat(factor).concat(paramsStr)
            .concat(factor).concat(reqBodyMd5);
        log.debug("string to signature: {}", string2Signature);
        return string2Signature;
    }

    /**
     * 构造签名
     *
     * @param signatureMethod  签名方法 HmacSHA256或HmacSHA1
     * @param secretAccessKey  私钥
     * @param string2Signature 待签名字符串
     * @return 签名
     */
    public static String generateSignature(String signatureMethod, String secretAccessKey, String string2Signature) {
        HmacAlgorithm hmacAlgorithm = "HmacSHA1".equals(signatureMethod) ? HmacAlgorithm.HmacSHA1
            : "HmacSHA256".equals(signatureMethod) ? HmacAlgorithm.HmacSHA256
            : null;
        Assert.notNull(hmacAlgorithm, "签名算法只支持HmacSHA256 或 HmacSHA1");
        byte[] digest = DigestUtil.hmac(hmacAlgorithm, secretAccessKey.getBytes(CHARSET)).digest(string2Signature);
        String signature = Base64.encode(digest);
        signature = URL_ENCODER.encode(signature, CHARSET);
        signature = URL_ENCODER.encode(signature, CHARSET);
        log.debug("signature: {}", signature);
        return signature;
    }

}
